package com.xyazm.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.xyazm.common.constants.RedisLockKeyConstants;
import com.xyazm.common.enums.*;
import com.xyazm.common.message.ActualRefundMessage;
import com.xyazm.common.redis.RedisLock;
import com.xyazm.common.utils.ParamCheckUtil;
import com.xyazm.common.utils.RandomUtil;
import com.xyazm.customer.api.ICustomerApi;
import com.xyazm.customer.domain.param.ReceiveAfterSaleAuditParam;
import com.xyazm.fulfill.api.IFulfillApi;
import com.xyazm.order.dao.*;
import com.xyazm.order.domain.dto.*;
import com.xyazm.order.domain.entity.*;
import com.xyazm.order.domain.param.ApplyAfterSaleParam;
import com.xyazm.order.domain.param.ReceiveCustomerAuditResultParam;
import com.xyazm.order.domain.param.RevokeAfterSaleParam;
import com.xyazm.order.enums.*;
import com.xyazm.order.enums.AmountTypeEnum;
import com.xyazm.order.exception.OrderBizException;
import com.xyazm.order.exception.OrderErrorCodeEnum;
import com.xyazm.order.factory.AfterSaleOperateLogFactory;
import com.xyazm.order.manager.MessageManager;
import com.xyazm.order.manager.impl.OrderNoManagerImpl;
import com.xyazm.order.mapstruct.OrderApiMapstruct;
import com.xyazm.order.mq.config.MQMessage;
import com.xyazm.order.mq.producer.AfterSaleApplySendActualRefundProducer;
import com.xyazm.order.mq.producer.CustomerAuditPassSendReleaseAssetsProducer;
import com.xyazm.order.mq.producer.DefaultProducer;
import com.xyazm.order.service.IApplyAfterSaleService;
import com.xyazm.order.service.ICancelOrderService;
import com.xyazm.order.statemachine.AfterSaleStateMachine;
import com.xyazm.order.statemachine.StateMachineFactory;
import com.xyazm.pay.api.IPayApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class ApplyAfterSaleServiceImpl implements IApplyAfterSaleService {

    @DubboReference(version = "1.0.0")
    private IPayApi payApi;
    @DubboReference(version = "1.0.0")
    private IFulfillApi fulfillApi;
    @DubboReference(version = "1.0.0")
    private ICustomerApi customerApi;

    @Autowired
    private OrderInfoDAO orderInfoDAO;
    @Autowired
    private OrderItemDAO orderItemDAO;
    @Autowired
    private OrderOperateLogDAO orderOperateLogDAO;
    @Autowired
    private RedisLock redisLock;
    @Autowired
    private DefaultProducer defaultProducer;
    @Autowired
    private AfterSaleInfoDAO afterSaleInfoDAO;
    @Autowired
    private AfterSaleItemDAO afterSaleItemDAO;
    @Autowired
    private AfterSaleLogDAO afterSaleLogDAO;
    @Autowired
    private AfterSaleRefundDAO afterSaleRefundDAO;
    @Autowired
    private OrderPaymentDetailDAO orderPaymentDetailDAO;
    @Autowired
    private OrderAmountDAO orderAmountDAO;
    @Autowired
    private AfterSaleOperateLogFactory afterSaleOperateLogFactory;
    @Autowired
    private OrderNoManagerImpl orderNoManager;
    @Autowired
    private MessageManager messageManager;
    @Autowired
    private ICancelOrderService cancelOrderService;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private AfterSaleApplySendActualRefundProducer afterSaleApplySendActualRefundProducer;
    @Autowired
    private CustomerAuditPassSendReleaseAssetsProducer customerAuditPassSendReleaseAssetsProducer;
    @Autowired
    private StateMachineFactory stateMachineFactory;

    /**
     * 当前业务限制说明：
     * 目前业务限定，一笔订单包含多笔订单条目，每次手动售后只能退一笔条目,不支持单笔条目多次退不同数量
     * <p>
     * 举例：
     * 一笔订单包含订单条目A（购买数量10）和订单条目B（购买数量1），每一次可单独发起 售后订单条目A or 售后订单条目B，
     * 如果是售后订单条目A，那么就是把A中购买数量10全部退掉
     * 如果是售后订单条目B，那么就是把B中购买数量1全部退款
     * <p>
     * 暂不支持第一次退A中的3条，第二次退A中的2条，第三次退A中的5条这种退法
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyAfterSale(ApplyAfterSaleParam param) {
        AfterSaleStateMachineDTO afterSaleStateMachineDTO = new AfterSaleStateMachineDTO();
        afterSaleStateMachineDTO.setApplyAfterSaleParam(param);
        // 售后状态机 操作 用户发起退货售后
        AfterSaleStateMachine afterSaleStateMachine = stateMachineFactory.getAfterSaleStateMachine(AfterSaleStatusEnum.UN_CREATED);
        // 取消订单 0 未创建 -> 10 提交申请
        afterSaleStateMachine.fire(AfterSaleStateChangeEnum.INITIATE_AFTER_SALE, afterSaleStateMachineDTO);
//        // 校验申请售后入参
//        checkApplyAfterSaleParam(param);
//        //  分布式锁
//        String orderId = param.getOrderId();
//        String key = RedisLockKeyConstants.REFUND_KEY + orderId;
//        boolean lock = redisLock.tryLock(key);
//        if (!lock) {
//            throw new OrderBizException(OrderErrorCodeEnum.PROCESS_AFTER_SALE_RETURN_GOODS);
//        }
//        try {
//            // 1.验证用户是否之前已经发起过售后申请
//             /*
//                场景校验逻辑：
//                第一种场景：订单条目A是第一次发起手动售后，此时售后订单条目表没有该订单的记录，afterSaleItems 是空，正常执行后面的售后逻辑
//                第二种场景：订单条目A已发起过售后，非"撤销成功"状态的售后单不允许重复发起售后
//             */
//            String skuCode = param.getSkuCode();
//            List<AfterSaleItemDO> afterSaleItems = afterSaleItemDAO.listByOrderIdAndSkuCode(orderId, skuCode);
//            if (CollectionUtils.isNotEmpty(afterSaleItems)) {
//                // 获取售后单id
//                String afterSaleId = afterSaleItems.get(0).getAfterSaleId();
//                //  业务校验：非"撤销成功"状态的售后单不能重复发起售后
//                AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(afterSaleId);
//                if (!AfterSaleStatusEnum.REVOKE.getCode().equals(afterSaleInfoDO.getAfterSaleStatus())) {
//                    throw new OrderBizException(OrderErrorCodeEnum.PROCESS_APPLY_AFTER_SALE_CANNOT_REPEAT);
//                }
//            }
//            // 2.构建申请售后DTO
//            ApplyAfterSaleDTO applyAfterSaleDTO = buildApplyAfterSaleDTO(param);
//            // 3.计算退货商品金额
//            applyAfterSaleDTO = calculateReturnGoodsAmount(applyAfterSaleDTO);
//            // 4.发送实际退款事务MQ消息
//            sendActualRefund(applyAfterSaleDTO);
//        }  finally {
//            redisLock.unlock(key);
//        }
    }


    /**
     * 发送实际退款事务MQ消息
     * @author xuyou
     * 2022-12-12 21:56
     * @param applyAfterSaleDTO
    */
    private void sendActualRefund(ApplyAfterSaleDTO applyAfterSaleDTO) {
        try {
            // 生成售后订单号
            String afterSaleId = orderNoManager.genOrderId(OrderNoTypeEnum.AFTER_SALE.getCode(),
                    applyAfterSaleDTO.getOrderInfoDTO().getUserId());
            applyAfterSaleDTO.setAfterSaleId(afterSaleId);
            TransactionMQProducer transactionMQProducer = afterSaleApplySendActualRefundProducer.getProducer();
            setSendActualRefundListener(transactionMQProducer);
            sendActualRefundSuccessMessage(transactionMQProducer, applyAfterSaleDTO, afterSaleId);
        } catch (Exception e) {
            throw new OrderBizException(OrderErrorCodeEnum.SEND_TRANSACTION_MQ_FAILED);
        }
    }

    private void setSendActualRefundListener(TransactionMQProducer transactionMQProducer) {
        transactionMQProducer.setTransactionListener(new TransactionListener() {
            @Override
            public LocalTransactionState executeLocalTransaction(Message message, Object o) {
                try {
                    ApplyAfterSaleDTO applyAfterSaleDTO = (ApplyAfterSaleDTO) o;
                    // 7、售后数据落库
                    insertApplyAfterSale(applyAfterSaleDTO);
                    return LocalTransactionState.COMMIT_MESSAGE;
                } catch (Exception e) {
                    log.error("system error", e);
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                }
            }

            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
                ReceiveAfterSaleAuditParam receiveAfterSaleAuditParam = JSON.parseObject(
                        new String(messageExt.getBody(), StandardCharsets.UTF_8), ReceiveAfterSaleAuditParam.class);
                String afterSaleId = receiveAfterSaleAuditParam.getAfterSaleId();
                //  查询售后数据是否插入成功
                AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(afterSaleId);
                if (afterSaleInfoDO != null) {
                    return LocalTransactionState.COMMIT_MESSAGE;
                }
                return LocalTransactionState.ROLLBACK_MESSAGE;
            }
        });
    }

    /**
     * 组装发送消息数据
     * @author xuyou
     * 2022-12-12 22:21
    */
    private void sendActualRefundSuccessMessage(TransactionMQProducer transactionMQProducer,
                                                ApplyAfterSaleDTO applyAfterSaleDTO, String afterSaleId) throws MQClientException {
        ReceiveAfterSaleAuditParam receiveAfterSaleAuditParam = new ReceiveAfterSaleAuditParam();
        receiveAfterSaleAuditParam.setOrderId(applyAfterSaleDTO.getOrderId());
        receiveAfterSaleAuditParam.setAfterSaleId(afterSaleId);
        receiveAfterSaleAuditParam.setAfterSaleRefundId(applyAfterSaleDTO.getAfterSaleRefundId());
        receiveAfterSaleAuditParam.setUserId(applyAfterSaleDTO.getUserId());
        receiveAfterSaleAuditParam.setAfterSaleType(applyAfterSaleDTO.getAfterSaleType());
        receiveAfterSaleAuditParam.setApplyRefundAmount(applyAfterSaleDTO.getApplyRefundAmount());
        receiveAfterSaleAuditParam.setReturnGoodAmount(applyAfterSaleDTO.getReturnGoodAmount());

        Message message = new MQMessage(RocketMqTopicEnum.AFTER_SALE_CUSTOMER_AUDIT.getTopic(),
                JSONObject.toJSONString(receiveAfterSaleAuditParam).getBytes(StandardCharsets.UTF_8));
        // 发起客服审核
        TransactionSendResult result = transactionMQProducer.sendMessageInTransaction(message, applyAfterSaleDTO);
        if (!result.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE)) {
            throw new OrderBizException(OrderErrorCodeEnum.SEND_AFTER_SALE_CUSTOMER_AUDIT_MQ_FAILED);
        }
    }



    @Override
    public void receiveCustomerAuditResult(ReceiveCustomerAuditResultParam param) {
        // 此处省略参数必填校验
        String afterSaleId = param.getAfterSaleId();
        Integer auditResult = param.getAuditResult();
        String orderId = param.getOrderId();
        // 根据售后单号查询售后单
        AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(afterSaleId);
        ParamCheckUtil.checkObjectNonNull(afterSaleInfoDO, OrderErrorCodeEnum.AFTER_SALE_NOT_FOUND);
        // 幂等校验：防止客服重复审核订单
        if (afterSaleInfoDO.getAfterSaleStatus() > AfterSaleStatusEnum.COMMITED.getCode()) {
            throw new OrderBizException(OrderErrorCodeEnum.CUSTOMER_AUDIT_CANNOT_REPEAT);
        }

        //  组装售后数据
        CustomerAuditResultDTO customerAuditResultDTO = buildCustomerAuditResultDTO(param);
        // 客服审核拒绝
        if (CustomerAuditResultEnum.REJECT.getCode().equals(auditResult)) {
            this.receiveCustomerAuditReject(customerAuditResultDTO, afterSaleInfoDO);
            return;
        }

        // 客服审核通过
        if (CustomerAuditResultEnum.ACCEPT.getCode().equals(auditResult)) {
            // 根据orderId和afterSaleId查询售后单条目
            AfterSaleItemDO afterSaleItemDO = afterSaleItemDAO.getByOrderIdAndAfterSaleId(orderId, afterSaleId);
            if (afterSaleItemDO == null) {
                throw new OrderBizException(OrderErrorCodeEnum.AFTER_SALE_ITEM_CANNOT_NULL);
            }
            // 组装释放资产MQ参数
            AuditAcceptReleaseAssetsDTO auditAcceptReleaseAssetsDTO = buildAuditAcceptReleaseAssetsDTO(afterSaleItemDO, customerAuditResultDTO);
            // 发送客服审核通过释放权益资产事务MQ
            sendAuditPassReleaseAssets(customerAuditResultDTO, auditAcceptReleaseAssetsDTO);
        }
    }

    /**
     * 发送客服审核通过释放权益资产事务MQ
     * @author xuyou
     * 2022-12-13 21:15
    */
    private void sendAuditPassReleaseAssets(CustomerAuditResultDTO customerAuditResultDTO,
                                            AuditAcceptReleaseAssetsDTO auditAcceptReleaseAssetsDTO) {
        try {
            TransactionMQProducer transactionMQProducer = customerAuditPassSendReleaseAssetsProducer.getProducer();
            setSendAuditPassReleaseAssetsListener(transactionMQProducer);
            sendAuditPassReleaseAssetsSuccessMessage(transactionMQProducer,
                    customerAuditResultDTO, auditAcceptReleaseAssetsDTO);
        } catch (Exception e) {
            throw new OrderBizException(OrderErrorCodeEnum.SEND_TRANSACTION_MQ_FAILED);
        }
    }


    private void setSendAuditPassReleaseAssetsListener(TransactionMQProducer transactionMQProducer) {
        transactionMQProducer.setTransactionListener(new TransactionListener() {
            @Override
            public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                try {
                    CustomerAuditResultDTO customerAuditResultDTO = (CustomerAuditResultDTO) arg;
                    //  更新 审核通过 售后信息
                    receiveCustomerAuditAccept(customerAuditResultDTO);
                    return LocalTransactionState.COMMIT_MESSAGE;
                } catch (Exception e) {
                    log.error("system error", e);
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                }
            }

            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                AuditAcceptReleaseAssetsDTO message = JSON.parseObject(
                        new String(msg.getBody(), StandardCharsets.UTF_8), AuditAcceptReleaseAssetsDTO.class);

                AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(
                        message.getActualRefundMessage().getAfterSaleId());
                Integer afterSaleStatus = afterSaleInfoDO.getAfterSaleStatus();
                if (AfterSaleStatusEnum.REVIEW_ACCEPT.getCode().equals(afterSaleStatus)) {
                    return LocalTransactionState.COMMIT_MESSAGE;
                }
                return LocalTransactionState.ROLLBACK_MESSAGE;
            }
        });

    }

    private void sendAuditPassReleaseAssetsSuccessMessage(TransactionMQProducer transactionMQProducer,
                                                          CustomerAuditResultDTO customerAuditResultDTO,
                                                          AuditAcceptReleaseAssetsDTO auditAcceptReleaseAssetsDTO)
            throws MQClientException {
        Message message = new MQMessage(RocketMqTopicEnum.CUSTOMER_AUDIT_ACCEPT_RELEASE_ASSETS_TOPIC.getTopic(),
                JSONObject.toJSONString(auditAcceptReleaseAssetsDTO).getBytes(StandardCharsets.UTF_8));
        TransactionSendResult result = transactionMQProducer.sendMessageInTransaction(message, customerAuditResultDTO);
        if (!result.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE)) {
            throw new OrderBizException(OrderErrorCodeEnum.SEND_AUDIT_PASS_RELEASE_ASSETS_FAILED);
        }

    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revokeAfterSale(RevokeAfterSaleParam param) {
        // 参数校验
        String afterSaleId = param.getAfterSaleId();
        ParamCheckUtil.checkObjectNonNull(afterSaleId, OrderErrorCodeEnum.AFTER_SALE_ID_IS_NULL);

        // 加锁,锁整个售后单,两个作用
        // 1.防并发
        // 2.业务上的考虑: 只要涉及售后表的更新，就需要加锁，锁整个售后表，否则算钱的时候，就会由于突然撤销，导致钱多算了
        String lockKey = RedisLockKeyConstants.REFUND_KEY + afterSaleId;
        boolean isLock = redisLock.tryLock(lockKey);
        if (!isLock) {
            throw new OrderBizException(OrderErrorCodeEnum.AFTER_SALE_CANNOT_REVOKE);
        }

        // 查询售后单
        AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(afterSaleId);
        ParamCheckUtil.checkObjectNonNull(afterSaleInfoDO, OrderErrorCodeEnum.AFTER_SALE_NOT_FOUND);

        // 校验售后单是否可以撤销：只有提交申请状态才可以撤销
        if (!AfterSaleStatusEnum.COMMITED.getCode().equals(afterSaleInfoDO.getAfterSaleStatus())) {
            throw new OrderBizException(OrderErrorCodeEnum.AFTER_SALE_CANNOT_REVOKE);
        }

        try {
            // 更新售后单状态为："已撤销"
            afterSaleInfoDAO.updateStatus(afterSaleId, AfterSaleStatusEnum.COMMITED.getCode(), AfterSaleStatusEnum.REVOKE.getCode());
            // 增加售后单操作日志
            AfterSaleLogDO afterSaleLogDO = afterSaleOperateLogFactory.buildAfterSaleLog(afterSaleInfoDO, AfterSaleStatusChangeEnum.AFTER_SALE_REVOKE);
            afterSaleLogDAO.save(afterSaleLogDO);
        } finally {
            redisLock.lock(lockKey);
        }
    }



    /**
     * 组装释放库存参数
     *
     * @return
     */
    private AuditAcceptReleaseAssetsDTO buildAuditAcceptReleaseAssetsDTO(AfterSaleItemDO afterSaleItemDO,
                                                                         CustomerAuditResultDTO customerAuditResultDTO) {
        String orderId = customerAuditResultDTO.getOrderId();
        String afterSaleId = customerAuditResultDTO.getAfterSaleId();

        AuditAcceptReleaseAssetsDTO auditAcceptReleaseAssetsDTO = new AuditAcceptReleaseAssetsDTO();

        // 释放库存数据
        List<ReleaseProductStockDTO.OrderItemDTO> orderItemDTOList = new ArrayList<>();
        ReleaseProductStockDTO.OrderItemDTO orderItemRequest = new ReleaseProductStockDTO.OrderItemDTO();
        orderItemRequest.setSkuCode(afterSaleItemDO.getSkuCode());
        orderItemRequest.setSaleQuantity(afterSaleItemDO.getReturnQuantity());
        orderItemDTOList.add(orderItemRequest);

        ReleaseProductStockDTO releaseProductStockDTO = new ReleaseProductStockDTO();
        releaseProductStockDTO.setOrderId(orderId);
        releaseProductStockDTO.setOrderItemDTOList(orderItemDTOList);

        auditAcceptReleaseAssetsDTO.setReleaseProductStockDTO(releaseProductStockDTO);

        // 实际退款数据
        ActualRefundMessage actualRefundMessage = new ActualRefundMessage();
        actualRefundMessage.setAfterSaleRefundId(customerAuditResultDTO.getAfterSaleRefundId());
        actualRefundMessage.setOrderId(orderId);
        actualRefundMessage.setAfterSaleId(afterSaleId);

         /*
            当前版本判断售后条目是否是订单所属的最后一条 业务限制：
            手动售后是整笔条目退，这里判断本次售后条目是否属于该订单的最后一个可售后的条目的逻辑是：
            如果 正向下单的订单条目总条数 = 售后已退款成功的订单条目数 + 1 （本次客服审核通过的这笔条目）
            那么 当前这笔审核通过的条目就是整笔订单的最后一条
         */
        //  判断是否是要退的最后一条
        List<OrderItemDO> orderItemDODOList = orderItemDAO.listByOrderId(orderId);
        //  查询售后订单条目表中不包含当前条目的数量
        List<AfterSaleItemDO> afterSaleItemDODOList = afterSaleItemDAO.listNotContainCurrentAfterSaleId(orderId, afterSaleId);
        //  查出的数据中是否包含当前正在审核的这条
        if (orderItemDODOList.size() == afterSaleItemDODOList.size() + 1) {
            //  本次条目就是当前订单的最后一笔
            actualRefundMessage.setLastReturnGoods(true);
        }
        auditAcceptReleaseAssetsDTO.setActualRefundMessage(actualRefundMessage);

        return auditAcceptReleaseAssetsDTO;
    }


    /**
     * 组装售后数据
     * @author xuyou
     * 2022/7/30 0030 11:23
     * @param param
     */
    private CustomerAuditResultDTO buildCustomerAuditResultDTO(ReceiveCustomerAuditResultParam param) {
        CustomerAuditResultDTO customerAuditAssembleRequest = new CustomerAuditResultDTO();
        customerAuditAssembleRequest.setOrderId(param.getOrderId());
        customerAuditAssembleRequest.setAfterSaleId(param.getAfterSaleId());
        customerAuditAssembleRequest.setAfterSaleRefundId(param.getAfterSaleRefundId());
        customerAuditAssembleRequest.setReviewReasonCode(param.getAuditResult());
        customerAuditAssembleRequest.setAuditResultDesc(param.getAuditResultDesc());
        // 客服审核来源 - 自营商城
        customerAuditAssembleRequest.setReviewSource(CustomerAuditSourceEnum.SELF_MALL.getCode());
        customerAuditAssembleRequest.setReviewTime(new Date());
        return customerAuditAssembleRequest;
    }


    /**
     * 接收客服审核拒绝结果
     * @author xuyou
     * 2022/7/30 0030 11:43
     * @param dto
     */
    private void receiveCustomerAuditReject(CustomerAuditResultDTO dto, AfterSaleInfoDO afterSaleInfoDO) {
        dto.setReviewReason(CustomerAuditResultEnum.REJECT.getMsg());
        transactionTemplate.execute(action -> {
            // 更新售后信息
            afterSaleInfoDAO.updateCustomerAuditAfterSaleResult(AfterSaleStatusEnum.REVIEW_REJECTED.getCode(), dto);
            // 记录售后日志
            AfterSaleLogDO afterSaleLogDO = afterSaleOperateLogFactory.buildAfterSaleLog(afterSaleInfoDO, AfterSaleStatusChangeEnum.AFTER_SALE_CUSTOMER_AUDIT_REJECT);
            afterSaleLogDAO.save(afterSaleLogDO);
            return null;
        });
    }

    /**
     * 接收客服审核通过结果
     * @author xuyou
     * 2022/7/30 0030 11:43
     * @param dto
     */
    private void receiveCustomerAuditAccept(CustomerAuditResultDTO dto) {
        dto.setReviewReason(CustomerAuditResultEnum.ACCEPT.getMsg());
        AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(dto.getAfterSaleId());
        //  幂等校验：防止客服重复审核订单
        if (afterSaleInfoDO.getAfterSaleStatus() > AfterSaleStatusEnum.COMMITED.getCode()) {
            return;
        }
        transactionTemplate.execute(action -> {
            afterSaleInfoDAO.updateCustomerAuditAfterSaleResult(AfterSaleStatusEnum.REVIEW_ACCEPT.getCode(), dto);
            //  记录售后日志
            AfterSaleLogDO afterSaleLogDO = afterSaleOperateLogFactory.buildAfterSaleLog(afterSaleInfoDO, AfterSaleStatusChangeEnum.AFTER_SALE_CUSTOMER_AUDIT_ACCEPT);
            afterSaleLogDAO.save(afterSaleLogDO);
            return null;
        });
    }

    /**
     * 查询售后信息的客服审核状态
     * @author xuyou
     * 2022/7/30 0030 12:29
     */
    private Integer findCustomerAuditAfterSaleStatus(String afterSaleId) {
        AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(afterSaleId);
        return afterSaleInfoDO.getAfterSaleStatus();
    }

    /**
     * 校验申请售后入参
     * @author xuyou
     * @date: 2022/7/13 21:27
     */
    private void checkApplyAfterSaleParam(ApplyAfterSaleParam param) {
        ParamCheckUtil.checkObjectNonNull(param);

        String orderId = param.getOrderId();
        ParamCheckUtil.checkStringNonEmpty(orderId, OrderErrorCodeEnum.ORDER_ID_IS_NULL);

        String userId = param.getUserId();
        ParamCheckUtil.checkStringNonEmpty(userId, OrderErrorCodeEnum.USER_ID_IS_NULL);

        Integer businessIdentifier = param.getBusinessIdentifier();
        ParamCheckUtil.checkObjectNonNull(businessIdentifier, OrderErrorCodeEnum.BUSINESS_IDENTIFIER_IS_NULL);

        Integer returnGoodsCode = param.getReturnGoodsCode();
        ParamCheckUtil.checkObjectNonNull(returnGoodsCode, OrderErrorCodeEnum.RETURN_GOODS_CODE_IS_NULL);

        String skuCode = param.getSkuCode();
        ParamCheckUtil.checkStringNonEmpty(skuCode, OrderErrorCodeEnum.SKU_IS_NULL);
    }


    /**
     * 构建申请售后DTO
     * @author xuyou
     * @date: 2022/7/13 21:44
     */
    private ApplyAfterSaleDTO buildApplyAfterSaleDTO(ApplyAfterSaleParam param) {

        String orderId = param.getOrderId();
        //  封装 订单信息
        OrderInfoDO orderInfoDO = orderInfoDAO.getByOrderId(orderId);
        OrderInfoDTO orderInfoDTO = OrderApiMapstruct.INSTANCE.convertOrderInfoDTO(orderInfoDO);

        //  封装 订单条目
        List<OrderItemDO> orderItemDOList = orderItemDAO.listByOrderId(orderId);
        List<OrderItemDTO> orderItemDTOList = OrderApiMapstruct.INSTANCE.convertOrderItemDTO(orderItemDOList);

        //  封装 订单售后条目
        List<AfterSaleItemDO> afterSaleItemDOList = afterSaleItemDAO.listByOrderId(orderId);
        List<AfterSaleItemDTO> afterSaleItemDTOList = OrderApiMapstruct.INSTANCE.convertAfterSaleItemDTO(afterSaleItemDOList);

        ApplyAfterSaleDTO build = ApplyAfterSaleDTO
                .builder()
                .orderId(orderId)
                .userId(param.getUserId())
                .skuCode(param.getSkuCode())
                .orderInfoDTO(orderInfoDTO)
                .orderItemDTOList(orderItemDTOList)
                .afterSaleItemDTOList(afterSaleItemDTOList)
                .build();
        return build;
    }

    /**
     * 计算退货商品金额
     * 一笔订单只有一个条目：整笔退
     * 一笔订单有多个条目，每次退一条，退完最后一条补退运费和优惠券
     * @author xuyou
     * 2022/7/19 0019 21:47
     */
    private ApplyAfterSaleDTO calculateReturnGoodsAmount(ApplyAfterSaleDTO applyAfterSaleDTO) {
        String orderId = applyAfterSaleDTO.getOrderId();
        List<OrderItemDTO> orderItemDTOList = applyAfterSaleDTO.getOrderItemDTOList();
        List<AfterSaleItemDTO> afterSaleItemDTOList = applyAfterSaleDTO.getAfterSaleItemDTOList();

        //  订单条目数
        int orderItemNum = orderItemDTOList.size();
        //  订单条目数，只有一条，退整笔 ，本次售后类型：全部退款
        if (orderItemNum == 1) {
            OrderItemDTO orderItemDTO = orderItemDTOList.get(0);
            return calculateWholeOrderRefundAmount(orderId,orderItemDTO.getPayAmount(),orderItemDTO.getOriginAmount(),applyAfterSaleDTO);
        }
        //  售后订单条目数
        int afterSaleItemNum = afterSaleItemDTOList.size();
        String skuCode = applyAfterSaleDTO.getSkuCode();
        //  skuCode和orderId查询本次要退的条目
        OrderItemDO orderItemDO = orderItemDAO.getOrderItemByOrderIdAndSkuCode(orderId, skuCode);
        //  该笔订单条目数 = 已存的售后订单条目数 + 本次退货的条目 (每次退 1 条)
        if (orderItemNum == afterSaleItemNum + 1) {
            //  当前条目是订单的最后一笔
            applyAfterSaleDTO = calculateWholeOrderRefundAmount(
                    orderId,
                    orderItemDO.getPayAmount(),
                    orderItemDO.getOriginAmount(),
                    applyAfterSaleDTO);
        } else {
            //  该笔订单还有其他条目,只退当前条目,本次售后类型：退货
            applyAfterSaleDTO.setAfterSaleType(AfterSaleTypeEnum.RETURN_GOODS.getCode());
            applyAfterSaleDTO.setReturnGoodAmount(orderItemDO.getPayAmount());
            applyAfterSaleDTO.setApplyRefundAmount(orderItemDO.getOriginAmount());
            applyAfterSaleDTO.setLastReturnGoods(false);
        }

        //  为便于以后扩展，这里封装成list
        List<OrderItemDTO> refundOrderItemDTOList = Lists.newArrayList();
        refundOrderItemDTOList.add(OrderApiMapstruct.INSTANCE.convertOrderItemDTO(orderItemDO));
        applyAfterSaleDTO.setRefundOrderItemDTO(refundOrderItemDTOList);
        return applyAfterSaleDTO;
    }


    private ApplyAfterSaleDTO calculateWholeOrderRefundAmount(String orderId, Integer payAmount,
                                                              Integer originAmount, ApplyAfterSaleDTO applyAfterSaleDTO) {
        // 模拟取运费
        OrderAmountDO deliveryAmount = orderAmountDAO.getOne(orderId, AmountTypeEnum.SHIPPING_AMOUNT.getCode());
        Integer freightAmount = (deliveryAmount == null || deliveryAmount.getAmount() == null) ? 0 : deliveryAmount.getAmount();
        //  最终退款金额 = 实际退款金额 + 运费
        Integer returnGoodAmount = payAmount + freightAmount;
        applyAfterSaleDTO.setReturnGoodAmount(returnGoodAmount);
        applyAfterSaleDTO.setApplyRefundAmount(originAmount);
        applyAfterSaleDTO.setAfterSaleType(AfterSaleTypeEnum.RETURN_MONEY.getCode());
        applyAfterSaleDTO.setLastReturnGoods(true);
        return applyAfterSaleDTO;
    }


    /**
     * 售后数据落库
     * @author xuyou
     * 2022/7/19 0019 22:08
     * @param applyAfterSaleDTO 申请售后DTO
     */
    private void insertApplyAfterSale(ApplyAfterSaleDTO applyAfterSaleDTO) {
        //  售后退货过程中的 申请退款金额 和 实际退款金额 是计算出来的，金额有可能不同
        AfterSaleInfoDO afterSaleInfoDO = new AfterSaleInfoDO();

        String orderId = applyAfterSaleDTO.getOrderId();
        String afterSaleId = applyAfterSaleDTO.getAfterSaleId();
        String userId = applyAfterSaleDTO.getUserId();

        transactionTemplate.execute(action -> {
            // 1.新增售后订单表
            insertAfterSaleInfoTableByApplyAfterSale(applyAfterSaleDTO,afterSaleInfoDO);
            // 2.新增售后条目表
            insertAfterSaleItemTableByApplyAfterSale(afterSaleId,orderId,applyAfterSaleDTO.getRefundOrderItemDTO());
            // 3.新增售后变更日志表
            insertAfterSaleLogTableByApplyAfterSale(afterSaleId,AfterSaleStatusEnum.UN_CREATED.getCode(),AfterSaleStatusEnum.COMMITED.getCode());
            // 4.新增售后支付表
            insertAfterSaleRefundTableByApplyAfterSale(afterSaleId, applyAfterSaleDTO.getOrderInfoDTO(), afterSaleInfoDO);
            return null;
        });


    }


    /**
     * 用户主动申请售后时添加售后支付表
     * @author xuyou
     * 2022/7/19 0019 22:29
     * @param afterSaleId
     * @param orderInfoDTO
     * @param afterSaleInfoDO
     */
    private void insertAfterSaleRefundTableByApplyAfterSale(String afterSaleId, OrderInfoDTO orderInfoDTO, AfterSaleInfoDO afterSaleInfoDO) {
        String orderId = orderInfoDTO.getOrderId();
        OrderPaymentDetailDO paymentDetail = orderPaymentDetailDAO.getPaymentDetailByOrderId(orderId);

        AfterSaleRefundDO afterSaleRefundDO = new AfterSaleRefundDO();
        afterSaleRefundDO.setAfterSaleId(afterSaleId);
        afterSaleRefundDO.setOrderId(orderId);
        // 账户类型 - 第三方
        afterSaleRefundDO.setAccountType(AccountTypeEnum.THIRD.getCode());
        // 退款状态 - 未退款
        afterSaleRefundDO.setRefundStatus(RefundStatusEnum.UN_REFUND.getCode());
        afterSaleRefundDO.setRemark(RefundStatusEnum.UN_REFUND.getMsg());
        afterSaleRefundDO.setRefundAmount(afterSaleInfoDO.getRealRefundAmount());
        afterSaleRefundDO.setAfterSaleBatchNo(orderId + RandomUtil.genRandomNumber(10));
        if (paymentDetail != null) {
            // 交易单号
            afterSaleRefundDO.setOutTradeNo(paymentDetail.getOutTradeNo());
            // 支付类型
            afterSaleRefundDO.setPayType(paymentDetail.getPayType());
        }
        afterSaleRefundDAO.save(afterSaleRefundDO);
        log.info("新增售后支付信息,订单号:{},售后单号:{},状态:{}", orderId, afterSaleId, afterSaleRefundDO.getRefundStatus());
    }

    /**
     * 用户主动申请售后时添加售后变更日志表
     * @author xuyou
     * 2022/7/19 0019 22:26
     * @param afterSaleId
     * @param preAfterSaleStatus
     * @param currentAfterSaleStatus
     */
    private void insertAfterSaleLogTableByApplyAfterSale(String afterSaleId, Integer preAfterSaleStatus, Integer currentAfterSaleStatus) {
        AfterSaleLogDO afterSaleLogDO = new AfterSaleLogDO();
        afterSaleLogDO.setAfterSaleId(afterSaleId);
        afterSaleLogDO.setPreStatus(preAfterSaleStatus);
        afterSaleLogDO.setCurrentStatus(currentAfterSaleStatus);
        //  售后退货的业务值
        afterSaleLogDO.setRemark(ReturnGoodsTypeEnum.AFTER_SALE_RETURN_GOODS.getMsg());
        afterSaleLogDAO.save(afterSaleLogDO);
        log.info("新增售后单变更信息, 售后单号:{},状态:PreStatus{},CurrentStatus:{}", afterSaleLogDO.getAfterSaleId(),
                afterSaleLogDO.getPreStatus(), afterSaleLogDO.getCurrentStatus());
    }

    /**
     * 用户主动申请售后时添加售后条目表
     * @author xuyou
     * 2022/7/19 0019 22:23
     * @param afterSaleId
     * @param orderId
     * @param refundOrderItemDTO
     */
    private void insertAfterSaleItemTableByApplyAfterSale(String afterSaleId, String orderId, List<OrderItemDTO> refundOrderItemDTO) {
        List<AfterSaleItemDO> afterSaleItemDOList = Lists.newArrayList();
        for (OrderItemDTO orderItem : refundOrderItemDTO) {
            AfterSaleItemDO afterSaleItemDO = new AfterSaleItemDO();
            afterSaleItemDO.setOrderId(orderId);
            afterSaleItemDO.setAfterSaleId(afterSaleId);
            afterSaleItemDO.setSkuCode(orderItem.getSkuCode());
            afterSaleItemDO.setProductName(orderItem.getProductName());
            afterSaleItemDO.setProductImg(orderItem.getProductImg());
            afterSaleItemDO.setReturnQuantity(orderItem.getSaleQuantity());
            afterSaleItemDO.setOriginAmount(orderItem.getOriginAmount());
            afterSaleItemDO.setApplyRefundAmount(orderItem.getOriginAmount());
            afterSaleItemDO.setRealRefundAmount(orderItem.getPayAmount());
            afterSaleItemDOList.add(afterSaleItemDO);
        }
        afterSaleItemDAO.saveBatch(afterSaleItemDOList);
    }

    /**
     * 用户主动申请售后时添加售后订单表
     * @author xuyou
     * 2022/7/19 0019 22:15
     */
    private void insertAfterSaleInfoTableByApplyAfterSale(ApplyAfterSaleDTO applyAfterSaleDTO,AfterSaleInfoDO afterSaleInfoDO) {


        // 售后订单号
        String afterSaleId = applyAfterSaleDTO.getAfterSaleId();
        OrderInfoDTO orderInfoDTO = applyAfterSaleDTO.getOrderInfoDTO();
        Integer afterSaleType = applyAfterSaleDTO.getAfterSaleType();


        afterSaleInfoDO.setApplyRefundAmount(applyAfterSaleDTO.getApplyRefundAmount());
        afterSaleInfoDO.setRealRefundAmount(applyAfterSaleDTO.getReturnGoodAmount());
        afterSaleInfoDO.setAfterSaleId(afterSaleId);
        afterSaleInfoDO.setOrderId(orderInfoDTO.getOrderId());
        afterSaleInfoDO.setUserId(orderInfoDTO.getUserId());
        // 接入方业务标识 - 自营商城
        afterSaleInfoDO.setBusinessIdentifier(BusinessIdentifierEnum.SELF_MALL.getCode());
        // 订单来源渠道 - 自营商城
        afterSaleInfoDO.setOrderSourceChannel(BusinessIdentifierEnum.SELF_MALL.getCode());
        // 订单类型 - 一般订单
        afterSaleInfoDO.setOrderType(OrderTypeEnum.NORMAL.getCode());
        afterSaleInfoDO.setApplyTime(new Date());

        // 用户售后退货的业务值
        // 售后单状态 - 提交申请
        afterSaleInfoDO.setAfterSaleStatus(AfterSaleStatusEnum.COMMITED.getCode());
        // 申请售后来源 - 用户申请退货
        afterSaleInfoDO.setApplySource(AfterSaleApplySourceEnum.USER_RETURN_GOODS.getCode());
        // 备注 - 售后退货
        afterSaleInfoDO.setRemark(ReturnGoodsTypeEnum.AFTER_SALE_RETURN_GOODS.getMsg());
        // 申请原因编码 - 用户自己原因
        afterSaleInfoDO.setApplyReasonCode(AfterSaleReasonEnum.USER.getCode());
        // 申请原因 - 用户自己原因
        afterSaleInfoDO.setApplyReason(AfterSaleReasonEnum.USER.getMsg());
        // 售后类型详情枚举 - 售后退货退款
        afterSaleInfoDO.setAfterSaleTypeDetail(AfterSaleTypeDetailEnum.PART_REFUND.getCode());
        // 售后类型 - 退款/退货
        afterSaleInfoDO.setAfterSaleType(afterSaleType);
        afterSaleInfoDAO.save(afterSaleInfoDO);
        log.info("新增订单售后记录,订单号:{},售后单号:{},订单售后状态:{}", orderInfoDTO.getOrderId(), afterSaleId,
                afterSaleInfoDO.getAfterSaleStatus());

    }
}
